﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Vds2465Telegrams
{
	public enum eMeasurementType
	{
		/// <summary>
		/// Temperatur, 01H, Celsius
		/// </summary>
		Temperature,
		/// <summary>
		/// Temperaturgradient, 03H, K7h
		/// </summary>
		TemperatureGradient,
		/// <summary>
		/// Lichtintensität, 04H, Lux
		/// </summary>
		LightIntensity,
		/// <summary>
		/// Windgeschwindigkeit, 05H, m/s
		/// </summary>
		WindSpeed,
		/// <summary>
		/// Druck, 06H, Pa
		/// </summary>
		Pressure,
		/// <summary>
		/// Feuchtigkeit, 07H, Prozent
		/// </summary>
		Humidity,
		/// <summary>
		/// Durchfluß, 08H, Liter/h
		/// </summary>
		FlowRate,
		/// <summary>
		/// Gasanteil, 09H, ppm
		/// </summary>
		GasQuota,
		/// <summary>
		/// Spannung 1, 20H, mVolt
		/// </summary>
		Tension1_mVolt,
		/// <summary>
		/// Spannung 2, 21H, Volt
		/// </summary>
		Tension2_Volt,
		/// <summary>
		/// Strom 1, 30H, mAmpere
		/// </summary>
		Current1_mAmpere,
		/// <summary>
		/// Strom 2, 31H, Ampere
		/// </summary>
		Current2_Ampere,
		/// <summary>
		/// Energie, 40H, kW/h
		/// </summary>
		Energy,
		/// <summary>
		/// Leistung, 48H, Watt
		/// </summary>
		Power,
		/// <summary>
		/// Radioaktive Energiedosis, 50H, l/kg
		/// </summary>
		RadioactiveEnergyDose,
	}

	public enum eRepresentationType
	{
		/// <summary>
		/// Used for temperature, light, pressure ... (0x01 .. 0x50
		/// </summary>
		RepType_01,

		/// <summary>
		/// Used for binary counters (0x80)
		/// </summary>
		RepType_02,

		/// <summary>
		/// Used for BCD counters (0x88)
		/// </summary>
		RepType_03,

		/// <summary>
		/// Used for user free definitions (0xF0 .. =xFF)
		/// </summary>
		RepType_Free
	}

	public class UserData_30_Measurements : UserData_AddressExt_1
	{
		#region constants

		public const byte MinFreeCode = 0xF0;

		#endregion constants

		#region private member variables

		private readonly eRepresentationType _representationType;

		// 01H..50H variant - representation type 01
		private readonly eMeasurementType _measurementType;
		private readonly double _measurementValue;

		// 80H variant - representation type 02
		private readonly byte _lowByte;
		private readonly byte _highByte;

		// 88H variant - representation type 03
		private readonly byte _byte01;
		private readonly byte _byte02;
		private readonly byte _byte03;

		// F0..FF variant - representation type free
		private readonly byte _freeCodeByte;
		private readonly byte[] _freeBytes;

		#endregion private member variables

		#region constructor

		/// <summary>
		/// The 01H - 50H variant
		/// </summary>
		public UserData_30_Measurements(byte device, byte address, byte addressAddOn, eAddressExtension_1 addressExtension, eMeasurementType measurementType, double measurementValue)
            : base(UserData.UserData30Measurements, device, address, addressAddOn, addressExtension)
        {
			_representationType = eRepresentationType.RepType_01;
			_measurementType = measurementType;
			_measurementValue = measurementValue;
		}
		
		/// <summary>
		/// The 80H variant
		/// </summary>
		/// <param name="lowByte">the lower 8 bits, values 0..255</param>
		/// <param name="highByte">the higher 8 bits, values 0..255 * 256</param>
		public UserData_30_Measurements(byte device, byte address, byte addressAddOn, eAddressExtension_1 addressExtension, byte lowByte, byte highByte)
			: base(UserData.UserData30Measurements, device, address, addressAddOn, addressExtension)
		{
			_representationType = eRepresentationType.RepType_02;
			_lowByte = lowByte;
			_highByte = highByte;
		}

		/// <summary>
		/// The 88H variant - number 200389 -> byte01 = 89, byte02 = 03, byte03 = 20
		/// </summary>
		/// <param name="byte01">the byte with number 2 and 1 (lowest two values)</param>
		/// <param name="byte02">the byte with number 4 and 3</param>
		/// <param name="byte03">the byte with number 6 and 5</param>
		public UserData_30_Measurements(byte device, byte address, byte addressAddOn, eAddressExtension_1 addressExtension, byte byte01, byte byte02, byte byte03)
			: base(UserData.UserData30Measurements, device, address, addressAddOn, addressExtension)
		{
			_representationType = eRepresentationType.RepType_03;
			_byte01 = byte01;
			_byte02 = byte02;
			_byte03 = byte03;
		}

		/// <summary>
		/// The F0..FFH variant
		/// </summary>
		public UserData_30_Measurements(byte device, byte address, byte addressAddOn, eAddressExtension_1 addressExtension, byte freeCodeByte, byte[] freeBytes)
			: base(UserData.UserData30Measurements, device, address, addressAddOn, addressExtension)
		{
			_representationType = eRepresentationType.RepType_Free;
			if (freeCodeByte < MinFreeCode)
			{
				// TODO handle error
			}

			_freeCodeByte = freeCodeByte;
			if (freeBytes != null)
			{
				_freeBytes = new byte[freeBytes.Length];
				for (int index = 0; index < freeBytes.Length; index++)
				{
					_freeBytes[index] = freeBytes[index];
				}
			}
			else
			{
				_freeBytes = new byte[0];
			}
		}

		#endregion constructor

        #region getters

		public eRepresentationType RepresentationType
		{
			get
			{
				return _representationType;
			}
		}

		/// <summary>
		/// 01H..50H variant - representation type 01
		/// </summary>
		public eMeasurementType MeasurementType
		{
			get
			{
				if (_representationType != eRepresentationType.RepType_01)
				{
					// TODO handle error - throw exception?
				}
				return _measurementType;
			}
		}

		/// <summary>
		/// 01H..50H variant - representation type 01
		/// </summary>
		public double MeasurementValue
		{
			get
			{
				if (_representationType != eRepresentationType.RepType_01)
				{
					// TODO handle error - throw exception?
				}
				return _measurementValue;
			}
		}

		/// <summary>
		/// 80H variant - representation type 02
		/// </summary>
		public byte LowByte
		{
			get
			{
				if (_representationType != eRepresentationType.RepType_02)
				{
					// TODO handle error - throw exception?
				}
				return _lowByte;
			}
		}

		/// <summary>
		/// 80H variant - representation type 02
		/// </summary>
		public byte HighByte
		{
			get
			{
				if (_representationType != eRepresentationType.RepType_02)
				{
					// TODO handle error - throw exception?
				}
				return _highByte;
			}
		}

		/// <summary>
		/// 88H variant - representation type 03
		/// </summary>
		public byte Byte01
		{
			get
			{
				if (_representationType != eRepresentationType.RepType_03)
				{
					// TODO handle error - throw exception?
				}
				return _byte01;
			}
		}

		/// <summary>
		/// 88H variant - representation type 03
		/// </summary>
		public byte Byte02
		{
			get
			{
				if (_representationType != eRepresentationType.RepType_03)
				{
					// TODO handle error - throw exception?
				}
				return _byte02;
			}
		}

		/// <summary>
		/// 88H variant - representation type 03
		/// </summary>
		public byte Byte03
		{
			get
			{
				if (_representationType != eRepresentationType.RepType_03)
				{
					// TODO handle error - throw exception?
				}
				return _byte03;
			}
		}

		/// <summary>
		/// F0..FF variant - representation type free
		/// </summary>
		public byte FreeCodeByte 
		{ 
			get 
			{
				if (_representationType != eRepresentationType.RepType_Free)
				{
					// TODO handle error - throw exception?
				}
				return _freeCodeByte; 
			} 
		}

		/// <summary>
		/// F0..FF variant - representation type free
		/// </summary>
		/// <returns></returns>
        public byte[] GetCopyOfFreeBytes()
        {
			if (_representationType != eRepresentationType.RepType_Free)
			{
				// TODO handle error - throw exception?
			}

			byte[] retFreeBytes = new byte[_freeBytes.Length];
			for (int index = 0; index < _freeBytes.Length; index++)
			{
				retFreeBytes[index] = _freeBytes[index];
			}

			return retFreeBytes;
        }	
        

        #endregion getters

        #region ToString, Equals, GetHashCode

		#region GetHashCode

        public override int GetHashCode()
        {
            int hashCode = base.GetHashCode();
			hashCode ^= (byte) _representationType;

			switch (_representationType)
			{
				case eRepresentationType.RepType_01:
					hashCode ^= GetHashCode_RepType01();
					break;
				case eRepresentationType.RepType_02:
					hashCode ^= GetHashCode_RepType02();
					break;
				case eRepresentationType.RepType_03:
					hashCode ^= GetHashCode_RepType03();
					break;
				case eRepresentationType.RepType_Free:
					hashCode ^= GetHashCode_RepTypeFree();
					break;
				default:
					// TODO handle error
					break;
			}
						
			return hashCode;
        }

		private int GetHashCode_RepType01()
		{
			int hashCode = (byte) _measurementType;
			hashCode ^= (byte) (_measurementValue / 0xff);
			hashCode ^= (byte) (_measurementValue % 0xff);
			return hashCode;
		}

		private int GetHashCode_RepType02()
		{
			int hashCode = _lowByte;
			hashCode ^= _highByte;
			return hashCode;
		}

		private int GetHashCode_RepType03()
		{
			int hashCode = _byte01;
			hashCode ^= _byte02;
			hashCode ^= _byte03;
			return hashCode;
		}

		private int GetHashCode_RepTypeFree()
		{
			int hashCode = _freeCodeByte;
			hashCode ^= UserDataCommon.GetHashCode_ByteArray(_freeBytes);
			return hashCode;
		}

		#endregion GetHashCode

		#region ToString

		public override string ToString()
        {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append("(");
            stringBuilder.Append(ToStringProperties());
            stringBuilder.Append(")");

            return stringBuilder.ToString();
        }

        internal override string ToStringProperties()
        {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append(base.ToStringProperties());

            stringBuilder.Append(", ");
			switch (_representationType)
			{
				case eRepresentationType.RepType_01:
					stringBuilder.Append(ToString_RepType01());
					break;
				case eRepresentationType.RepType_02:
					stringBuilder.Append(ToString_RepType02());
					break;
				case eRepresentationType.RepType_03:
					stringBuilder.Append(ToString_RepType03());
					break;
				case eRepresentationType.RepType_Free:
					stringBuilder.Append(ToString_RepTypeFree());
					break;
				default:
					// TODO handle error
					break;
			}            

            return stringBuilder.ToString();
        }

		private string ToString_RepType01()
		{
			StringBuilder stringBuilder = new StringBuilder();

			stringBuilder.Append("MeasurementType='");
			stringBuilder.Append(MeasurementType);
			stringBuilder.Append("'");

			stringBuilder.Append(", ");

			stringBuilder.Append("MeasurementValue='");
			stringBuilder.Append(MeasurementValue);
			stringBuilder.Append("'");

			return stringBuilder.ToString();
		}

		private string ToString_RepType02()
		{
			StringBuilder stringBuilder = new StringBuilder();

			stringBuilder.Append("LowByte='");
			stringBuilder.Append("0x");
			stringBuilder.Append(LowByte.ToString("X2"));
			stringBuilder.Append("'");

			stringBuilder.Append(", ");

			stringBuilder.Append("HighByte='");
			stringBuilder.Append("0x");
			stringBuilder.Append(HighByte.ToString("X2"));
			stringBuilder.Append("'");

			return stringBuilder.ToString();
		}

		private string ToString_RepType03()
		{
			StringBuilder stringBuilder = new StringBuilder();

			stringBuilder.Append("Byte01='");
			stringBuilder.Append("0x");
			stringBuilder.Append(Byte01.ToString("X2"));
			stringBuilder.Append("'");

			stringBuilder.Append(", ");

			stringBuilder.Append("Byte02='");
			stringBuilder.Append("0x");
			stringBuilder.Append(Byte02.ToString("X2"));
			stringBuilder.Append("'");

			stringBuilder.Append(", ");

			stringBuilder.Append("Byte03='");
			stringBuilder.Append("0x");
			stringBuilder.Append(Byte03.ToString("X2"));
			stringBuilder.Append("'");

			return stringBuilder.ToString();
		}

		private string ToString_RepTypeFree()
		{
			StringBuilder stringBuilder = new StringBuilder();

			stringBuilder.Append("FreeCodeByte='");
			stringBuilder.Append("0x");
			stringBuilder.Append(FreeCodeByte.ToString("X2"));
			stringBuilder.Append("'");

			stringBuilder.Append(", ");

			stringBuilder.Append(UserDataCommon.HexByteArrayToString("FreeBytes", _freeBytes));

			return stringBuilder.ToString();			
		}

		#endregion ToString

		#region Equals

		public override bool Equals(object obj)
        {
            Type objType = obj.GetType();
            if (this.GetType() == objType)
            {
				UserData_30_Measurements userData2Compare = obj as UserData_30_Measurements;
                return EqualsProperties(userData2Compare);
            }

            return false;
        }

		internal bool EqualsProperties(UserData_30_Measurements userData2Compare)
        {
            if (base.EqualsProperties(userData2Compare))
            {
				if (_representationType == userData2Compare.RepresentationType)
				{
					switch (_representationType)
					{
						case eRepresentationType.RepType_01:
							return EqualsProperties_RepType01(userData2Compare);
						case eRepresentationType.RepType_02:
							return EqualsProperties_RepType02(userData2Compare);
						case eRepresentationType.RepType_03:
							return EqualsProperties_RepType03(userData2Compare);
						case eRepresentationType.RepType_Free:
							return EqualsProperties_RepTypeFree(userData2Compare);
						default:
							// TODO handle error
							return false;
					}			
				}

				return false;
            }

            return false;
		}

		private bool EqualsProperties_RepType01(UserData_30_Measurements userData2Compare)
		{
			if (_measurementType == userData2Compare.MeasurementType)
			{
				return _measurementValue == userData2Compare.MeasurementValue;
			}

			return false;
		}

		private bool EqualsProperties_RepType02(UserData_30_Measurements userData2Compare)
		{
			if (_lowByte == userData2Compare.LowByte)
			{
				return _highByte == userData2Compare.HighByte;
			}

			return false;
		}

		private bool EqualsProperties_RepType03(UserData_30_Measurements userData2Compare)
		{
			if (_byte01 == userData2Compare.Byte01)
			{
				if (_byte02 == userData2Compare.Byte02)
				{
					return _byte03 == userData2Compare.Byte03;
				}

				return false;
			}
			
			return false;
		}

		private bool EqualsProperties_RepTypeFree(UserData_30_Measurements userData2Compare)
		{
			if (_freeCodeByte == userData2Compare.FreeCodeByte)
			{
				return UserDataCommon.Equals2ByteArrays(_freeBytes, userData2Compare.GetCopyOfFreeBytes());
			}

			return false;
		}

		#endregion Equals
		
		#endregion ToString, Equals, GetHashCode
	}
}
